#include <iostream>
#include <string>
#include <iomanip>
#include <vector>
#include <limits>
#include <sstream>
#include "IntegerFormatter.h"

using namespace std;
const int arrSize = 7;
int arr[arrSize] = {0,1,2,-12,42,std::numeric_limits<int>::min(),std::numeric_limits<int>::max()};
vector<int> vec = vector<int>(arr,arr+arrSize);

void print(IntegerFormatter & form, int i);
vector<int> readInts(string str, IntegerFormatter & form);
vector<int> readInts(string str);
IntegerFormatter globalForm();
void dummy();

int main()
{
    cout << IntegerFormatter()
        .setWidth(10)
        .setPadding('0')
        .setBase(16)
        .setShowBase()
        .out(42) << endl; // Print 000002A_16
    IntegerFormatter form = IntegerFormatter().setBase(16); // Print numbers in hexadecimal
    cout << "form = IntegerFormatter().setBase(16)" << endl;
    for(std::vector<int>::iterator it = vec.begin() ; it < vec.end() ; ++it)
    {
        print(form,*it);
    }
    cout <<  endl;

    form = IntegerFormatter().setBase(2).setWidth(40); // Print numbers in binary with fixed width
    cout << "form = IntegerFormatter().setBase(2).setWidth(40)" << endl;
    for(std::vector<int>::iterator it = vec.begin() ; it < vec.end() ; ++it)
    {
        print(form,*it);
    }
    cout << endl;
    
    string str = " 007  10  20  ---  30  42 0xA abc 1";
    form = IntegerFormatter().setBase(10); // Read numbers expecting decimal numbers
    cout << "form = IntegerFormatter().setBase(10)" << endl;
    vector<int> tempVec = readInts(str,form);
    for(std::vector<int>::iterator it = tempVec.begin() ; it < tempVec.end() ; ++it)
    {
        print(form,*it);
    }
    cout << endl;

    cout << "No form (expecting the same as above)" << endl;
    tempVec = readInts(str,form); // Reading numbers using standard reading (no formatting object involved)
    for(std::vector<int>::iterator it = tempVec.begin() ; it < tempVec.end() ; ++it)
    {
        print(form,*it);
    }
    cout << endl;

    str = "-EfG a";
    form = IntegerFormatter().setBase(20).setIgnoreCase(); // Ignore case is actually default
    cout << "form = IntegerFormatter().setBase(20).setIgnoreCase()" << endl;
    tempVec = readInts(str,form);
    for(std::vector<int>::iterator it = tempVec.begin() ; it < tempVec.end() ; ++it)
    {
        print(form,*it);
    }
    cout << endl;
    
    form = IntegerFormatter().setBase(20).setUpperCase(); // Now we only accept upper case characters as part of our integer
    cout << "form = IntegerFormatter().setBase(20).setUpperCase()" << endl;
    tempVec = readInts(str,form);
    for(std::vector<int>::iterator it = tempVec.begin() ; it < tempVec.end() ; ++it)
    {
        print(form,*it);
    }
    cout << endl;

    stringbuf buffer;
    ostream os(&buffer); // Binding output stream to buffer
    istream is(&buffer); // Binding input stream to the same buffer
    string tempStr = "-";
    for(int i = 0; i < 10 ; ++i)
    {
        // The stream can now work as a queue, where the ostream is producer and istream worker
        os << tempStr;
        is >> tempStr;
    }
    cout << buffer.str() << endl;
    
    
    
    //cout << IntegerFormatter().setBase(37)(36) << endl; // <- Compiles, but throws exception
    
    
    
    dummy(); // <- A few additional examples, e.g. with usage of cin



    cin.get();
    return 0;
}

void print(IntegerFormatter & form, int i)
{
    ostringstream stream;
    stream << "form.out("<<i<<"): ";
    cout << setw(25) << stream.str() << form.out(i) << endl;
}

std::vector<int> readInts(std::string str, IntegerFormatter & form)
{
    cout << "Reading string with form: '" << str << "'" << endl;
    istringstream stream(str);
    vector<int> res;
    int i;
    while(stream)
    {
        stream >> form.in(i);
        res.push_back(i);
    }
    return res;
}

std::vector<int> readInts(std::string str)
{
    cout << "Reading string without form: '" << str << "'" << endl;
    istringstream stream(str);
    vector<int> res;
    int i;
    while(stream)
    {
        stream >> i;
        res.push_back(i);
    }
    return res;
}

IntegerFormatter globalForm()
{
    static IntegerFormatter form = IntegerFormatter().setBase(16).setWidth(20).setPadding('0');
    return form;
}

// Only used to collectcode for the report
void dummy()
{
    cout.width(10);
    cout.fill('_');
    cout.setf(ios_base::hex,ios_base::basefield);
    cout.setf(ios_base::showbase);
    cout << 42 << endl; // Prints: '______0x2a'

    
    cout<< setw(10) 
        << setfill('_') 
        << setbase(16) 
        << setiosflags (ios_base::showbase) 
        << 42 << endl; // Prints: '______0x2a'

    cout<< setbase(10);

    int i;
    cin >> IntegerFormatter().setBase(2).in(i);
    cout << i << endl; // Print value of i (in decimal)

    IntegerFormatter local = IntegerFormatter().setBase(2);
    cout << "Global out: " << globalForm().out(42) << endl;
    cout << "Local out: " << local.out(42) << endl;
    cout << "Global altered out: " << globalForm().setBase(20).out(42) << endl;
}